package com.company.JUC.Lock;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Wait queue node class.
 *
 *
 *



 The wait queue is a variant of a "CLH" (Craig, Landin, and
 Hagersten) lock queue.

 CLH锁通常用于自旋锁。
 CLH locks are normally used for spinlocks.

 We instead use them for blocking synchronizers, but
 use the same basic tactic of holding some of the control
 information about a thread in the predecessor of its node.

 "status"字段用于跟踪每个节点是否有线程应该阻止。
 A "status" field in each node keeps track of whether a thread should block.

 A node is signalled when its predecessor
 releases.   Each node of the queue otherwise serves as a
 specific-notification-style monitor holding a single waiting
 thread.

 状态字段不控制线程是否为授予锁等等。
 The status field does NOT control whether threads are granted locks etc though.

如果 一个线程排在队列的头部,线程可以尝试获取锁
 A thread may try to acquire if it is first in the queue.
 但成为第一并不能保证成功;
 But being first does not guarantee success;

 它只给予了竞争的权利。
 it only gives the right to contend.

 所以当前的发布的竞争线程可能需要重新等待
 So the currently released contender thread may need to rewait.


 To enqueue into a CLH lock, you atomically splice it in as new
tail. To dequeue, you just set the head field.

 获取锁失败进行入队操作，获取锁成功进行出队操作。
      +------+  prev +-----+       +-----+

 head |      | <---- |     | <---- |     |  tail

      +------+       +-----+       +-----+





 Insertion into a CLH queue requires only a single atomic
operation on "tail", so there is a simple atomic point of
demarcation from unqueued to queued. Similarly, dequeuing
involves only updating the "head". However, it takes a bit
more work for nodes to determine who their successors are,
in part to deal with possible cancellation due to timeouts
and interrupts.


 The "prev" links (not used in original CLH locks), are mainly
 needed to handle cancellation. If a node is cancelled, its
 successor is (normally) relinked to a non-cancelled
 predecessor. For explanation of similar mechanics in the case
 of spin locks, see the papers by Scott and Scherer at
 http://www.cs.rochester.edu/u/scott/synchronization/


 We also use "next" links to implement blocking mechanics.
 The thread id for each node is kept in its own node, so a
 predecessor signals the next node to wake up by traversing
 next link to determine which thread it is.   Determination of
 successor must avoid races with newly queued nodes to set
 the "next" fields of their predecessors.   This is solved
 when necessary by checking backwards from the atomically
 updated "tail" when a node's successor appears to be null.
 (Or, said differently, the next-links are an optimization
 so that we don't usually need a backward scan.)


 Cancellation introduces some conservatism to the basic
algorithms.   Since we must poll for cancellation of other
nodes, we can miss noticing whether a cancelled node is
ahead or behind us. This is dealt with by always unparking
successors upon cancellation, allowing them to stabilize on
a new predecessor, unless we can identify an uncancelled
predecessor who will carry this responsibility.


 CLH queues need a dummy header node to get started. But
we don't create them on construction, because it would be wasted
effort if there is never contention. Instead, the node
is constructed and head and tail pointers are set upon first
contention.


 Threads waiting on Conditions use the same nodes, but
use an additional link. Conditions only need to link nodes
in simple (non-concurrent) linked queues because they are
only accessed when exclusively held.   Upon await, a node is
inserted into a condition queue.   Upon signal, the node is
transferred to the main queue.   A special value of status
field is used to mark which queue a node is on.


 Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
 Scherer and Michael Scott, along with members of JSR-166
 expert group, for helpful ideas, discussions, and critiques
 on the design of this class.

/**
 * @Classname LockTest
 * @Description TODO
 * 实现同步组件时推荐定义继承AQS的静态内存类，并重写需要的protected修饰的方法；
 * 同步组件语义的实现依赖于AQS的模板方法，而AQS模板方法又依赖于被AQS的子类所重写的方法。
 * @Date 2021/8/18 16:57
 * @Created by ylr
 */
public class LockTest implements Lock, Serializable {
    // 静态内部类
    // 继承AQS的静态内存类
    // 重写方法
    private static class Sync extends AbstractQueuedSynchronizer{

        //如果状态为零，则获取锁
        @Override
        protected boolean tryAcquire(int acquire) {
            assert acquire==1; //否则不能使用
            if (compareAndSetState(0,1)){

                //设置当前拥有独占访问权的线程。null参数表示没有线程拥有访问权限。此方法不会强制执行任何同步或volatile字段访问线程的所有者线程
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        //通过将状态设置为零来释放锁
        @Override
        protected boolean tryRelease(int release) {
            assert release==1;
            if (getState()==0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        @Override
        protected boolean isHeldExclusively() {
            return getState()==1;
        }

        // Provides a Condition
        Condition newCondition() {
            return new ConditionObject();
        }

        // Deserializes properly
        private void readObject(ObjectInputStream s)
                throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    private final  Sync sync =new Sync();

    @Override
    public void lock() {
        //先看同步状态是否获取成功，如果成功则方法结束返回
        //若失败则先调用addWaiter()方法 :1. 将当前线程构建成Node类型   2.2 将当前节点尾插入的方式插入同步队列中
        // 再调用acquireQueued()方法
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1)   ;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1,unit.toNanos(time))   ;
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }

    public boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }


    private static LockTest mutex = new LockTest();
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                mutex.lock();
                try {
                    System.out.println(Thread.currentThread().getName()+" 开始");
                    Thread.sleep(3000);
                    System.out.println(Thread.currentThread().getName()+" 结束");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    mutex.unlock();
                }
            });
            thread.start();
        }

    }
}
